package com.hqd.ch03.v51.context.annotation;

import com.hqd.ch03.utils.ClassUtils;
import com.hqd.ch03.v51.core.annotation.AnnotationAttributes;
import com.hqd.ch03.v51.core.env.Environment;
import com.hqd.ch03.v51.core.type.filter.AbstractTypeHierarchyTraversingFilter;
import com.hqd.ch03.v51.core.type.filter.TypeFilter;
import com.hqd.ch03.v51.factory.config.BeanDefinitionHolder;
import com.hqd.ch03.v51.factory.support.BeanNameGenerator;
import com.hqd.ch03.v51.io.ResourceLoader;
import com.hqd.ch03.v51.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v51.utils.BeanUtils;
import com.hqd.ch03.v51.utils.StringUtils;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class ComponentScanAnnotationParser {

    /**
     * 环境
     */
    private final Environment environment;
    /**
     * 资源加载
     */
    private final ResourceLoader resourceLoader;
    /**
     * bean名称生成器
     */
    private final BeanNameGenerator beanNameGenerator;
    /**
     * BD注册器
     */
    private final BeanDefinitionRegistry registry;


    public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader,
                                         BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {

        this.environment = environment;
        this.resourceLoader = resourceLoader;
        this.beanNameGenerator = beanNameGenerator;
        this.registry = registry;
    }


    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {

        /**
         * 类路径扫描器
         */
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

        /**
         *
         * 获取bd名称生成器
         */
        Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
        boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
        scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
                BeanUtils.instantiateClass(generatorClass));

        ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
        if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
            scanner.setScopedProxyMode(scopedProxyMode);
        } else {
            Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
            scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
        }
        //设置资源匹配器
        scanner.setResourcePattern(componentScan.getString("resourcePattern"));

        /**
         * 导入过滤器
         */
        for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
            List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
                    this.resourceLoader, this.registry);
            for (TypeFilter typeFilter : typeFilters) {
                scanner.addIncludeFilter(typeFilter);
            }
        }
        /**
         *排除过滤器
         */
        for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
            List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
                    this.resourceLoader, this.registry);
            for (TypeFilter typeFilter : typeFilters) {
                scanner.addExcludeFilter(typeFilter);
            }
        }

        boolean lazyInit = componentScan.getBoolean("lazyInit");
        if (lazyInit) {
            scanner.getBeanDefinitionDefaults().setLazyInit(true);
        }
        //获取基础包路径
        Set<String> basePackages = new LinkedHashSet<>();
        String[] basePackagesArray = componentScan.getStringArray("basePackages");
        for (String pkg : basePackagesArray) {
            String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                    ",; \t\n");
            Collections.addAll(basePackages, tokenized);
        }
        for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(declaringClass));
        }

        scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
            @Override
            protected boolean matchClassName(String className) {
                return declaringClass.equals(className);
            }
        });
        return scanner.doScan(StringUtils.toStringArray(basePackages));
    }

}

